if(!require("knitr")){
  install.packages("knitr")
  library(knitr)
}
knitr::opts_chunk$set(echo = TRUE, fig.width=20)

N’OUBLIEZ PAS DE RENSEIGNER LE NOM DE L’OEUVRE

0.1 Préparer la session de travail

if(!require("knitr")){
  install.packages("knitr")
  library(knitr)
}
knitr::opts_chunk$set(echo = TRUE, fig.width=20)

1 0. Préparation du texte

1.1 Udpipe

Il va falloire télécharger le package UDpipe. Pour le français du XVIIème s. nous conseillons french-gsd, même s’il est (très) loin d’être parfait.

setwd("~/GitHub/Cours_2020_UniGE/Cours_Geneve_13")

1.2 Nom du texte

Vous pouvez ici rentrer le nom du texte que nous allons étudier. Le nom que vous allez mettre va être reproduit sur tous les graphiques: attention! Enter the name of the text that will be studied (it is the one that will be printed on all the graphs produced here), and the name of the file

if (!require("udpipe")) install.packages("udpipe")
library(udpipe)

1.3 Préparation de la lemmatisation

Nous allons lemamtiser le texte à l’aide dun modèle UDPipe. Il est en effet plus facile de travailler sur des lemmes pour diminuer la taille du lexique.

Si vous n’avez pas de modèles (il y en a un dans le dossier), vous pouvez les télécharger à cet endroit.

If you do not have an udpipe model, you can download one here (and adjust infra the name)

text_title <- "Boyer - Agamemnon (1680)"
file_name<- "BOYER_AGAMEMNON_1680/BOYER_AGAMEMNON_1680.txt"
#Il pesut être utile de créer un dossier spécifique, pour mettre toutes les images produites dans cet endroit
folder_name <-"BOYER_AGAMEMNON_1680/"

Nous pouvons désormais charger le texte, et le modèle.

#m<-udpipe_download_model(language =  "french-gsd")
#m<-udpipe_download_model(language =  "french-partut")
#m<-udpipe_download_model(language =  "french-sequoia")

2 Lemmatisation

udpipe_model_french <- udpipe_load_model(file = "tools/french-gsd-ud-2.4-190531.udpipe")
text_to_lemmatise <- readLines(file_name)
#variable pour les sauvegardes
#nom de fichier sans extension
file_name_noExt <- tools::file_path_sans_ext(file_name)
#ajout d'une extention
filename <- paste(file_name_noExt,"_LEM.txt", sep="")

Je sauvegarde le texte

#on transforme le texte en une longue chaîne de caractères séparés par des espaces.
text_to_lemmatise <- paste(text_to_lemmatise, collapse = " ")
# on lemmatise cette chaîne
x <- udpipe_annotate(udpipe_model_french, x = text_to_lemmatise)
#je fais un data.frame
x <- as.data.frame(x)
# voir un extrait
str(x)
'data.frame':   17093 obs. of  14 variables:
 $ doc_id       : chr  "doc1" "doc1" "doc1" "doc1" ...
 $ paragraph_id : int  1 1 1 1 1 1 1 1 1 1 ...
 $ sentence_id  : int  1 1 1 1 1 1 1 1 1 1 ...
 $ sentence     : chr  "oui, pylade, il est vrai, la valeur et l'adresse ont de l'asie enfin fait triompher la grèce." "oui, pylade, il est vrai, la valeur et l'adresse ont de l'asie enfin fait triompher la grèce." "oui, pylade, il est vrai, la valeur et l'adresse ont de l'asie enfin fait triompher la grèce." "oui, pylade, il est vrai, la valeur et l'adresse ont de l'asie enfin fait triompher la grèce." ...
 $ token_id     : chr  "1" "2" "3" "4" ...
 $ token        : chr  "oui" "," "pylade" "," ...
 $ lemma        : chr  "oui" "," "pylade" "," ...
 $ upos         : chr  "INTJ" "PUNCT" "NOUN" "PUNCT" ...
 $ xpos         : chr  NA NA NA NA ...
 $ feats        : chr  NA NA "Gender=Fem|Number=Sing" NA ...
 $ head_token_id: chr  "14" "1" "7" "3" ...
 $ dep_rel      : chr  "advmod" "punct" "obl:mod" "punct" ...
 $ deps         : chr  NA NA NA NA ...
 $ misc         : chr  "SpacesBefore=\\s\\s\\s\\s\\s\\s\\t|SpaceAfter=No" NA "SpaceAfter=No" NA ...

3 1. Vers l’analyse de sentiment

3.1 Téélcharger et charger le package syuzhet

cat(x$lemma[1], "", file = filename)
for(i in 2:length(x$token_id)){
  if(x$sentence_id[i] != x$sentence_id[i-1])
    cat("\n", file = filename, append = T)
  if(is.na(x$lemma[i]))
    next
  cat(x$lemma[i], "", file = filename, append = T)
}

3.2 Charger un lexique

Amine Abdaoui, Jérôme Azé, Sandra Bringay et Pascal Poncelet. “FEEL: French Expanded Emotion Lexicon. Language Resources and Evaluation”, LRE 2016, pp 1-23. Cf. http://www.lirmm.fr/~abdaoui/FEEL

Cf. aussi the package R rfeel: https://github.com/ColinFay/rfeel

if (!require("syuzhet")) install.packages("syuzhet")
library(syuzhet)

On peut jeter un petit coup d’œil à ces lexiques

FEEL = read.csv("lexicons/FEEL-1_clean.csv")
load("lexicons/sentiments_polarity.rda")
load("lexicons/sentiments_score.rda")

3.3 Importer le texte

Il faut désormais importer le texte que nous allons étudier

View(FEEL)
View(sentiments_polarity)
View(sentiments_score)

4 2. Polarity scores

4.1 Création du vecteur

Nous utilisons la méthode de Bing pour étudier la polarité: chaque token est comparé à un lexique, où des mots sont catégorés comme positifs ou négatifs.

text_to_analyse <- readLines(filename)
head(text_to_analyse)
[1] "oui , pylade , il être vrai , le valeur et le adresse avoir de le asie enfin faire triompher le grèce . "                                                                             
[2] "de tout côté en foule on venir dans ce cour , croire de agamemnon célébrer le retour , et toi-même suivant le zèle qui ter guide , pour voler dans mycène avoir quitter le phocide . "
[3] "cependant cher ami , son soin être superflure . "                                                                                                                                     
[4] "le troyen être venger : son père ne vivre plus . "                                                                                                                                    
[5] "il ne être plus ! "                                                                                                                                                                   
[6] "ô disgrâce à jamais déplorable ! "                                                                                                                                                    

Nous pouvons donc désormais proposer une première visualisation

text_to_analyse_bing_vector <- get_sentiment(text_to_analyse, method = "bing", lexicon = FEEL)
head(text_to_analyse_bing_vector, 300)
#summary(text_to_analyse_bing_vector)

4.2 Normalisation de la visualisation

Nous avons besoin de normaliser la longueur de notre image pour pouvoir comparer des textes de longueurs différentes. On peut aussi diminuer la taille des chunks pour amplifier les tendances.

Avec une base 100

simple_plot(text_to_analyse_bing_vector, title = paste(text_title, " - BING"))

# on sauvegarde l'image
png(paste(file_name_noExt,"_BING.png", sep=""), height = 900, width = 1600, res = 100)
simple_plot(text_to_analyse_bing_vector, title =  paste(text_title, " - BING"))
dev.off()

Avec une base 10

percent_vals <- get_percentage_values(text_to_analyse_bing_vector, bins = 100)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 100", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
#Saving the graph
png(paste(file_name_noExt,"_BASE100.png"), height = 900, width = 1600, res = 100)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 100", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
dev.off()

Avec une base 2

percent_vals <- get_percentage_values(text_to_analyse_bing_vector, bins = 10)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 10", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
#Saving the graph
png(paste(file_name_noExt,"_BASE10.png"), height = 900, width = 1600, res = 100)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 100", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
dev.off()

On peut obtenir une tendance générale avec summary

percent_vals <- get_percentage_values(text_to_analyse_bing_vector, bins = 2)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 2", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
#Saving the graph
png(paste(file_name_noExt,"_BASE2.png"), height = 900, width = 1600, res = 100)
plot(
  percent_vals, 
  type="l", 
  main=paste(text_title,"base 2", sep=" "), 
  xlab = "Narrative Time", 
  ylab= "Emotional Valence", 
  col="red"
  )
dev.off()
quartz_off_screen 
                2 

5 3. Etudier les sentiments

5.1 La joie

5.1.1 Nous allons customier le dictionnaire

Pour étudier la joie, nous sortons la colonne joie du lexique pour créer un petit sous-lexique

sum(text_to_analyse_bing_vector)
[1] -232
summary(text_to_analyse_bing_vector)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.0000 -1.0000  0.0000 -0.2375  0.0000  2.0000 

Je reproduis là même manipulation que supra

custom_dict_joy <- data.frame(word=FEEL$word, value=FEEL$joy)
head(custom_dict_joy)

5.1.2 Un peu d’ingénierie

Qu’est-ce qu’il y a sous le capot

100% center

100% center

nous allons regarder les mots qui ont été utilisés pendant l’analyse, classés par fréquence

text_to_analyse_vector_joy <- get_sentiment(text_to_analyse, method = "custom", lexicon = custom_dict_joy)
simple_plot(text_to_analyse_vector_joy, title = paste(text_title, "Joy", sep=" - "))
#Saving the graph
png(paste(file_name_noExt,"_JOY.png"), height = 900, width = 1600, res = 100)
simple_plot(text_to_analyse_vector_joy, title = paste(text_title, "Joy", sep=" - "))
dev.off()
quartz_off_screen 
                2 

Je peux extraire tous les mots utiles pour l’analye

text_to_analyse_tokenised <- table(unlist(strsplit(text_to_analyse, "\\W")))
head(sort(text_to_analyse_tokenised, decreasing = T), n=50)

                 le        de       son        il       que        et        ce        un         à      être     avoir       lui 
     4700      1063       814       794       416       398       333       324       299       293       250       206       171 
     vous      tout        je        ne        en      pour      dans        se      quel      mais        si     faire       par 
      163       153       149       144       138       137       123       108       104        97        97        96        83 
     plus       soi cassandre       qui      dieu      fils        me     aller      voir      trop     amour   pouvoir        on 
       80        80        74        73        64        64        62        61        61        57        56        56        55 
 seigneur        ah       sur   vouloir      elle       roi      oeil     coeur    devoir      père     hymen 
       55        54        54        54        53        49        45        44        44        43        39 

5.2 Attention!!

Regardons les mots qui sont catégorisés comme de la joie d’aprps FEEL

positive_words=character()
positive_words_custom=character()
for(i in 1:length(text_to_analyse_tokenised)){
  # identify positive words
  if(get_sentiment(names(text_to_analyse_tokenised[i]), method = "custom", lexicon = custom_dict_joy) > 0){
    positive_words = c(positive_words, names(text_to_analyse_tokenised[i]))
    positive_words_custom = c(positive_words_custom, (text_to_analyse_tokenised[i]*get_sentiment(names(text_to_analyse_tokenised[i]), method = "custom", lexicon = custom_dict_joy)))
  }
}
head(sort(positive_words_custom, decreasing = T), n=50)
      triomphe         offrir         songer         enfant        premier          repos      triompher        auguste        obtenir 
           "8"            "7"            "6"            "5"            "5"            "5"            "5"            "4"            "4" 
    victorieux           bien        bonheur        combler        douceur      favorable          mutin       rappeler        enfance 
           "4"           "34"            "3"            "3"            "3"            "3"            "3"            "3"            "2" 
         libre      reprendre     satisfaire        superbe       survivre           joie        heureux          fille        vaincre 
           "2"            "2"            "2"            "2"            "2"           "13"           "11"           "10"           "10" 
           vie       amoureux        avancer     avantageux       flatteur         gagner   heureusement        silence          songe 
          "10"            "1"            "1"            "1"            "1"            "1"            "1"            "1"            "1" 
tranquillement 
           "1" 

Le lexique est (vraiment) loin d’êtr parfait: des mots comme content ou fier ne sont pas considéré comme relevant de la joie!

head(FEEL$word[which(FEEL$joy==1)], n=50)
 [1] avantageux        batifoler         bénigne           bien              bienheureux       capiteux          chanceux         
 [8] clément           comique           commode           communicatif      consolateur       conte             convenance       
[15] décontracter      délice            délicieux         divertir          duveteux          éclat             existence        
[22] faisable          favorable         ferveur           festin            festif            flatteur          folle|fou de joie
[29] fructueux         futé              gazouillis        gemme             heureux           inventif          jovial           
[36] joyeusement       libidineux        lucratif          lumineux          luxueux           majesté           miséricordieux   
[43] nonchalamment     nonchalant        officiant         opportun          pétillement       plein été         précieux         
[50] premier          
14126 Levels: ?cuménique ?il ?illet ?uf ?uf de poisson ?uvre ?uvre classique @card@ kilo à base de plante à bord à capuche ... zut

Et donc la phrase “je suis heureux, content et fier” (ou plutôt je être heureux, content et fier) a un score de 1.

FEEL$joy[which(FEEL$word=="joie")]
[1] 1
FEEL$joy[which(FEEL$word=="heureux")]
[1] 1
FEEL$joy[which(FEEL$word=="content")]
[1] 0
FEEL$joy[which(FEEL$word=="fier")]
[1] 0

5.3 Peur

Je peux recommencer la même étude, mais cette fois avec le lexique de la peur

#je rentre mon texte
text_to_analyse_test <-"je suis heureux, content et fier"
#Je tokenise
text_to_analyse_tokenised_test <- table(unlist(strsplit(text_to_analyse_test, "\\W")))
#Je remets mes variables à zero
positive_words=character()
positive_words_custom=character()
#Je cherche dans le lexique
#Pour chaque mot de ma phrase
for(i in 1:length(text_to_analyse_tokenised_test)){
  # si un mot a une valeur supérieure à zéro dans mon lexique
  if(get_sentiment(names(text_to_analyse_tokenised_test[i]), method = "custom", lexicon = custom_dict_joy) > 0){
    #Je l'ajoute aux mots positifs
    positive_words = c(positive_words, names(text_to_analyse_tokenised_test[i]))
    positive_words_custom = c(positive_words_custom, (text_to_analyse_tokenised_test[i]*get_sentiment(names(text_to_analyse_tokenised_test[i]), method = "custom", lexicon = custom_dict_joy)))
  }
}
positive_words_custom
heureux 
    "1" 

Et donc reprodurie une même visualisation à partir de ce nouveau sous-lexique

custom_dict_fear <- data.frame(word=FEEL$word, value=FEEL$fear)

Extraire les mots utilisés pour l’analyse

#{r, fig.width=6, fig.height=, dpi=10}
text_to_analyse_vector_fear <- get_sentiment(text_to_analyse, method = "custom", lexicon = custom_dict_fear)
simple_plot(text_to_analyse_vector_fear, title = paste(text_title, "Fear", sep=" - "))
#Saving the graph
png(paste(file_name_noExt,"_FEAR.png"), height = 900, width = 1600, res = 100)
simple_plot(text_to_analyse_vector_fear, title = paste(text_title, "Fear", sep=" - "))
dev.off()
quartz_off_screen 
                2 

`

5.3.1 References

Un grand merci à Simone Rebora (Università di Verona/DHLab Basel) pour son aide.

LS0tCnRpdGxlOiAiQ291cnNfMjEiCmF1dGhvcjogIlNpbW9uIEdhYmF5IgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCmBgYHtyLCBzZXR1cCwgZmlnLnNob3c9aG9sZCwgZmlnLm1hcmdpbj1UUlVFfQppZighcmVxdWlyZSgia25pdHIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogIGxpYnJhcnkoa25pdHIpCn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcud2lkdGg9MjApCmBgYAoKKioKTidPVUJMSUVaIFBBUyBERSBSRU5TRUlHTkVSIExFIE5PTSBERSBMJ09FVVZSRQoqKgoKIyMgUHLDqXBhcmVyIGxhIHNlc3Npb24gZGUgdHJhdmFpbAoKYGBge3J9CnNldHdkKCJ+L0dpdEh1Yi9Db3Vyc18yMDIwX1VuaUdFL0NvdXJzX0dlbmV2ZV8xMyIpCmBgYAoKIyAwLiBQcsOpcGFyYXRpb24gZHUgdGV4dGUKCiMjIFVkcGlwZQoKSWwgdmEgZmFsbG9pcmUgdMOpbMOpY2hhcmdlciBsZSBwYWNrYWdlIGBVRHBpcGVgLiBQb3VyIGxlIGZyYW7Dp2FpcyBkdSBYVklJw6htZSBzLiBub3VzIGNvbnNlaWxsb25zIGBmcmVuY2gtZ3NkYCwgbcOqbWUgcydpbCBlc3QgKHRyw6hzKSBsb2luIGQnw6p0cmUgcGFyZmFpdC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KaWYgKCFyZXF1aXJlKCJ1ZHBpcGUiKSkgaW5zdGFsbC5wYWNrYWdlcygidWRwaXBlIikKbGlicmFyeSh1ZHBpcGUpCmBgYAoKIyMgTm9tIGR1IHRleHRlCgpWb3VzIHBvdXZleiBpY2kgcmVudHJlciBsZSBub20gZHUgdGV4dGUgcXVlIG5vdXMgYWxsb25zIMOpdHVkaWVyLiBMZSBub20gcXVlIHZvdXMgYWxsZXogbWV0dHJlIHZhIMOqdHJlIHJlcHJvZHVpdCBzdXIgdG91cyBsZXMgZ3JhcGhpcXVlczogYXR0ZW50aW9uIQpFbnRlciB0aGUgbmFtZSBvZiB0aGUgdGV4dCB0aGF0IHdpbGwgYmUgc3R1ZGllZCAoaXQgaXMgdGhlIG9uZSB0aGF0IHdpbGwgYmUgcHJpbnRlZCBvbiBhbGwgdGhlIGdyYXBocyBwcm9kdWNlZCBoZXJlKSwgYW5kIHRoZSBuYW1lIG9mIHRoZSBmaWxlCgpgYGB7cn0KdGV4dF90aXRsZSA8LSAiQm95ZXIgLSBBZ2FtZW1ub24gKDE2ODApIgpmaWxlX25hbWU8LSAiQk9ZRVJfQUdBTUVNTk9OXzE2ODAvQk9ZRVJfQUdBTUVNTk9OXzE2ODAudHh0IgojSWwgcGVzdXQgw6p0cmUgdXRpbGUgZGUgY3LDqWVyIHVuIGRvc3NpZXIgc3DDqWNpZmlxdWUsIHBvdXIgbWV0dHJlIHRvdXRlcyBsZXMgaW1hZ2VzIHByb2R1aXRlcyBkYW5zIGNldCBlbmRyb2l0CmZvbGRlcl9uYW1lIDwtIkJPWUVSX0FHQU1FTU5PTl8xNjgwLyIKYGBgCgojIyBQcsOpcGFyYXRpb24gZGUgbGEgbGVtbWF0aXNhdGlvbgoKTm91cyBhbGxvbnMgbGVtYW10aXNlciBsZSB0ZXh0ZSDDoCBsJ2FpZGUgZHVuIG1vZMOobGUgYFVEUGlwZWAuIElsIGVzdCBlbiBlZmZldCBwbHVzIGZhY2lsZSBkZSB0cmF2YWlsbGVyIHN1ciBkZXMgbGVtbWVzIHBvdXIgZGltaW51ZXIgbGEgdGFpbGxlIGR1IGxleGlxdWUuCgpTaSB2b3VzIG4nYXZleiBwYXMgZGUgbW9kw6hsZXMgKGlsIHkgZW4gYSB1biBkYW5zIGxlIGRvc3NpZXIpLCB2b3VzIHBvdXZleiBsZXMgdMOpbMOpY2hhcmdlciDDoCBjZXQgZW5kcm9pdC4KCklmIHlvdSBkbyBub3QgaGF2ZSBhbiB1ZHBpcGUgbW9kZWwsIHlvdSBjYW4gZG93bmxvYWQgb25lIGhlcmUgKGFuZCBhZGp1c3QgX2luZnJhXyB0aGUgbmFtZSkKYGBge3J9CiNtPC11ZHBpcGVfZG93bmxvYWRfbW9kZWwobGFuZ3VhZ2UgPSAgImZyZW5jaC1nc2QiKQojbTwtdWRwaXBlX2Rvd25sb2FkX21vZGVsKGxhbmd1YWdlID0gICJmcmVuY2gtcGFydHV0IikKI208LXVkcGlwZV9kb3dubG9hZF9tb2RlbChsYW5ndWFnZSA9ICAiZnJlbmNoLXNlcXVvaWEiKQpgYGAKCk5vdXMgcG91dm9ucyBkw6lzb3JtYWlzIGNoYXJnZXIgbGUgdGV4dGUsIGV0IGxlIG1vZMOobGUuCgpgYGB7cn0KdWRwaXBlX21vZGVsX2ZyZW5jaCA8LSB1ZHBpcGVfbG9hZF9tb2RlbChmaWxlID0gInRvb2xzL2ZyZW5jaC1nc2QtdWQtMi40LTE5MDUzMS51ZHBpcGUiKQp0ZXh0X3RvX2xlbW1hdGlzZSA8LSByZWFkTGluZXMoZmlsZV9uYW1lKQojdmFyaWFibGUgcG91ciBsZXMgc2F1dmVnYXJkZXMKI25vbSBkZSBmaWNoaWVyIHNhbnMgZXh0ZW5zaW9uCmZpbGVfbmFtZV9ub0V4dCA8LSB0b29sczo6ZmlsZV9wYXRoX3NhbnNfZXh0KGZpbGVfbmFtZSkKI2Fqb3V0IGQndW5lIGV4dGVudGlvbgpmaWxlbmFtZSA8LSBwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9MRU0udHh0Iiwgc2VwPSIiKQpgYGAKCiMgTGVtbWF0aXNhdGlvbgoKYGBge3J9CiNvbiB0cmFuc2Zvcm1lIGxlIHRleHRlIGVuIHVuZSBsb25ndWUgY2hhw65uZSBkZSBjYXJhY3TDqHJlcyBzw6lwYXLDqXMgcGFyIGRlcyBlc3BhY2VzLgp0ZXh0X3RvX2xlbW1hdGlzZSA8LSBwYXN0ZSh0ZXh0X3RvX2xlbW1hdGlzZSwgY29sbGFwc2UgPSAiICIpCiMgb24gbGVtbWF0aXNlIGNldHRlIGNoYcOubmUKeCA8LSB1ZHBpcGVfYW5ub3RhdGUodWRwaXBlX21vZGVsX2ZyZW5jaCwgeCA9IHRleHRfdG9fbGVtbWF0aXNlKQojamUgZmFpcyB1biBkYXRhLmZyYW1lCnggPC0gYXMuZGF0YS5mcmFtZSh4KQojIHZvaXIgdW4gZXh0cmFpdApzdHIoeCkKYGBgCgpKZSBzYXV2ZWdhcmRlIGxlIHRleHRlCmBgYHtyfQpjYXQoeCRsZW1tYVsxXSwgIiIsIGZpbGUgPSBmaWxlbmFtZSkKZm9yKGkgaW4gMjpsZW5ndGgoeCR0b2tlbl9pZCkpewogIGlmKHgkc2VudGVuY2VfaWRbaV0gIT0geCRzZW50ZW5jZV9pZFtpLTFdKQogICAgY2F0KCJcbiIsIGZpbGUgPSBmaWxlbmFtZSwgYXBwZW5kID0gVCkKICBpZihpcy5uYSh4JGxlbW1hW2ldKSkKICAgIG5leHQKICBjYXQoeCRsZW1tYVtpXSwgIiIsIGZpbGUgPSBmaWxlbmFtZSwgYXBwZW5kID0gVCkKfQpgYGAKCiMgMS4gVmVycyBsJ2FuYWx5c2UgZGUgc2VudGltZW50CgojIyBUw6nDqWxjaGFyZ2VyIGV0IGNoYXJnZXIgbGUgcGFja2FnZSBzeXV6aGV0CgpgYGB7ciwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CmlmICghcmVxdWlyZSgic3l1emhldCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJzeXV6aGV0IikKbGlicmFyeShzeXV6aGV0KQpgYGAKCiMjIENoYXJnZXIgdW4gbGV4aXF1ZQoKQW1pbmUgQWJkYW91aSwgSsOpcsO0bWUgQXrDqSwgU2FuZHJhIEJyaW5nYXkgZXQgUGFzY2FsIFBvbmNlbGV0LiAiRkVFTDogRnJlbmNoIEV4cGFuZGVkIEVtb3Rpb24gTGV4aWNvbi4gTGFuZ3VhZ2UgUmVzb3VyY2VzIGFuZCBFdmFsdWF0aW9uIiwgX0xSRSAyMDE2XywgcHAgMS0yMy4gQ2YuIGh0dHA6Ly93d3cubGlybW0uZnIvfmFiZGFvdWkvRkVFTAoKQ2YuIGF1c3NpIHRoZSBwYWNrYWdlIFIgYHJmZWVsYDogaHR0cHM6Ly9naXRodWIuY29tL0NvbGluRmF5L3JmZWVsCgpgYGB7cn0KRkVFTCA9IHJlYWQuY3N2KCJsZXhpY29ucy9GRUVMLTFfY2xlYW4uY3N2IikKbG9hZCgibGV4aWNvbnMvc2VudGltZW50c19wb2xhcml0eS5yZGEiKQpsb2FkKCJsZXhpY29ucy9zZW50aW1lbnRzX3Njb3JlLnJkYSIpCmBgYAoKT24gcGV1dCBqZXRlciB1biBwZXRpdCBjb3VwIGQnxZNpbCDDoCBjZXMgbGV4aXF1ZXMKCmBgYHtyfQpWaWV3KEZFRUwpClZpZXcoc2VudGltZW50c19wb2xhcml0eSkKVmlldyhzZW50aW1lbnRzX3Njb3JlKQpgYGAKCiMjIEltcG9ydGVyIGxlIHRleHRlCgpJbCBmYXV0IGTDqXNvcm1haXMgaW1wb3J0ZXIgbGUgdGV4dGUgcXVlIG5vdXMgYWxsb25zIMOpdHVkaWVyCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnRleHRfdG9fYW5hbHlzZSA8LSByZWFkTGluZXMoZmlsZW5hbWUpCmhlYWQodGV4dF90b19hbmFseXNlKQpgYGAKCiMgMi4gUG9sYXJpdHkgc2NvcmVzCgojIyBDcsOpYXRpb24gZHUgdmVjdGV1cgpOb3VzIHV0aWxpc29ucyBsYSBtw6l0aG9kZSBkZSBCaW5nIHBvdXIgw6l0dWRpZXIgbGEgcG9sYXJpdMOpOiBjaGFxdWUgdG9rZW4gZXN0IGNvbXBhcsOpIMOgIHVuIGxleGlxdWUsIG/DuSBkZXMgbW90cyBzb250IGNhdMOpZ29yw6lzIGNvbW1lIHBvc2l0aWZzIG91IG7DqWdhdGlmcy4KCmBgYHtyfQp0ZXh0X3RvX2FuYWx5c2VfYmluZ192ZWN0b3IgPC0gZ2V0X3NlbnRpbWVudCh0ZXh0X3RvX2FuYWx5c2UsIG1ldGhvZCA9ICJiaW5nIiwgbGV4aWNvbiA9IEZFRUwpCmhlYWQodGV4dF90b19hbmFseXNlX2JpbmdfdmVjdG9yLCAzMDApCiNzdW1tYXJ5KHRleHRfdG9fYW5hbHlzZV9iaW5nX3ZlY3RvcikKYGBgCgpOb3VzIHBvdXZvbnMgZG9uYyBkw6lzb3JtYWlzIHByb3Bvc2VyIHVuZSBwcmVtacOocmUgdmlzdWFsaXNhdGlvbgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpzaW1wbGVfcGxvdCh0ZXh0X3RvX2FuYWx5c2VfYmluZ192ZWN0b3IsIHRpdGxlID0gcGFzdGUodGV4dF90aXRsZSwgIiAtIEJJTkciKSkKCiMgb24gc2F1dmVnYXJkZSBsJ2ltYWdlCnBuZyhwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9CSU5HLnBuZyIsIHNlcD0iIiksIGhlaWdodCA9IDkwMCwgd2lkdGggPSAxNjAwLCByZXMgPSAxMDApCnNpbXBsZV9wbG90KHRleHRfdG9fYW5hbHlzZV9iaW5nX3ZlY3RvciwgdGl0bGUgPSAgcGFzdGUodGV4dF90aXRsZSwgIiAtIEJJTkciKSkKZGV2Lm9mZigpCmBgYAoKIyMgTm9ybWFsaXNhdGlvbiBkZSBsYSB2aXN1YWxpc2F0aW9uCgpOb3VzIGF2b25zIGJlc29pbiBkZSBub3JtYWxpc2VyIGxhIGxvbmd1ZXVyIGRlIG5vdHJlIGltYWdlIHBvdXIgcG91dm9pciBjb21wYXJlciBkZXMgdGV4dGVzIGRlIGxvbmd1ZXVycyBkaWZmw6lyZW50ZXMuIE9uIHBldXQgYXVzc2kgZGltaW51ZXIgbGEgdGFpbGxlICBkZXMgY2h1bmtzIHBvdXIgYW1wbGlmaWVyIGxlcyB0ZW5kYW5jZXMuCgpBdmVjIHVuZSBiYXNlIDEwMAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpwZXJjZW50X3ZhbHMgPC0gZ2V0X3BlcmNlbnRhZ2VfdmFsdWVzKHRleHRfdG9fYW5hbHlzZV9iaW5nX3ZlY3RvciwgYmlucyA9IDEwMCkKcGxvdCgKICBwZXJjZW50X3ZhbHMsIAogIHR5cGU9ImwiLCAKICBtYWluPXBhc3RlKHRleHRfdGl0bGUsImJhc2UgMTAwIiwgc2VwPSIgIiksIAogIHhsYWIgPSAiTmFycmF0aXZlIFRpbWUiLCAKICB5bGFiPSAiRW1vdGlvbmFsIFZhbGVuY2UiLCAKICBjb2w9InJlZCIKICApCiNTYXZpbmcgdGhlIGdyYXBoCnBuZyhwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9CQVNFMTAwLnBuZyIpLCBoZWlnaHQgPSA5MDAsIHdpZHRoID0gMTYwMCwgcmVzID0gMTAwKQpwbG90KAogIHBlcmNlbnRfdmFscywgCiAgdHlwZT0ibCIsIAogIG1haW49cGFzdGUodGV4dF90aXRsZSwiYmFzZSAxMDAiLCBzZXA9IiAiKSwgCiAgeGxhYiA9ICJOYXJyYXRpdmUgVGltZSIsIAogIHlsYWI9ICJFbW90aW9uYWwgVmFsZW5jZSIsIAogIGNvbD0icmVkIgogICkKZGV2Lm9mZigpCmBgYAoKQXZlYyB1bmUgYmFzZSAgMTAgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnBlcmNlbnRfdmFscyA8LSBnZXRfcGVyY2VudGFnZV92YWx1ZXModGV4dF90b19hbmFseXNlX2JpbmdfdmVjdG9yLCBiaW5zID0gMTApCnBsb3QoCiAgcGVyY2VudF92YWxzLCAKICB0eXBlPSJsIiwgCiAgbWFpbj1wYXN0ZSh0ZXh0X3RpdGxlLCJiYXNlIDEwIiwgc2VwPSIgIiksIAogIHhsYWIgPSAiTmFycmF0aXZlIFRpbWUiLCAKICB5bGFiPSAiRW1vdGlvbmFsIFZhbGVuY2UiLCAKICBjb2w9InJlZCIKICApCiNTYXZpbmcgdGhlIGdyYXBoCnBuZyhwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9CQVNFMTAucG5nIiksIGhlaWdodCA9IDkwMCwgd2lkdGggPSAxNjAwLCByZXMgPSAxMDApCnBsb3QoCiAgcGVyY2VudF92YWxzLCAKICB0eXBlPSJsIiwgCiAgbWFpbj1wYXN0ZSh0ZXh0X3RpdGxlLCJiYXNlIDEwMCIsIHNlcD0iICIpLCAKICB4bGFiID0gIk5hcnJhdGl2ZSBUaW1lIiwgCiAgeWxhYj0gIkVtb3Rpb25hbCBWYWxlbmNlIiwgCiAgY29sPSJyZWQiCiAgKQpkZXYub2ZmKCkKYGBgCgpBdmVjIHVuZSBiYXNlIDIKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KcGVyY2VudF92YWxzIDwtIGdldF9wZXJjZW50YWdlX3ZhbHVlcyh0ZXh0X3RvX2FuYWx5c2VfYmluZ192ZWN0b3IsIGJpbnMgPSAyKQpwbG90KAogIHBlcmNlbnRfdmFscywgCiAgdHlwZT0ibCIsIAogIG1haW49cGFzdGUodGV4dF90aXRsZSwiYmFzZSAyIiwgc2VwPSIgIiksIAogIHhsYWIgPSAiTmFycmF0aXZlIFRpbWUiLCAKICB5bGFiPSAiRW1vdGlvbmFsIFZhbGVuY2UiLCAKICBjb2w9InJlZCIKICApCiNTYXZpbmcgdGhlIGdyYXBoCnBuZyhwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9CQVNFMi5wbmciKSwgaGVpZ2h0ID0gOTAwLCB3aWR0aCA9IDE2MDAsIHJlcyA9IDEwMCkKcGxvdCgKICBwZXJjZW50X3ZhbHMsIAogIHR5cGU9ImwiLCAKICBtYWluPXBhc3RlKHRleHRfdGl0bGUsImJhc2UgMiIsIHNlcD0iICIpLCAKICB4bGFiID0gIk5hcnJhdGl2ZSBUaW1lIiwgCiAgeWxhYj0gIkVtb3Rpb25hbCBWYWxlbmNlIiwgCiAgY29sPSJyZWQiCiAgKQpkZXYub2ZmKCkKYGBgCgpPbiBwZXV0IG9idGVuaXIgdW5lIHRlbmRhbmNlIGfDqW7DqXJhbGUgYXZlYyBgc3VtbWFyeWAKCmBgYHtyfQpzdW0odGV4dF90b19hbmFseXNlX2JpbmdfdmVjdG9yKQpzdW1tYXJ5KHRleHRfdG9fYW5hbHlzZV9iaW5nX3ZlY3RvcikKYGBgCgojIDMuIEV0dWRpZXIgbGVzIHNlbnRpbWVudHMKCiMjIExhIGpvaWUKCiMjIyBOb3VzIGFsbG9ucyBjdXN0b21pZXIgbGUgZGljdGlvbm5haXJlCgpQb3VyIMOpdHVkaWVyIGxhIGpvaWUsIG5vdXMgc29ydG9ucyBsYSBjb2xvbm5lIGpvaWUgZHUgbGV4aXF1ZSBwb3VyIGNyw6llciB1biBwZXRpdCBzb3VzLWxleGlxdWUKCmBgYHtyfQpjdXN0b21fZGljdF9qb3kgPC0gZGF0YS5mcmFtZSh3b3JkPUZFRUwkd29yZCwgdmFsdWU9RkVFTCRqb3kpCmhlYWQoY3VzdG9tX2RpY3Rfam95KQpgYGAKCkplIHJlcHJvZHVpcyBsw6AgbcOqbWUgbWFuaXB1bGF0aW9uIHF1ZSBfc3VwcmFfCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnRleHRfdG9fYW5hbHlzZV92ZWN0b3Jfam95IDwtIGdldF9zZW50aW1lbnQodGV4dF90b19hbmFseXNlLCBtZXRob2QgPSAiY3VzdG9tIiwgbGV4aWNvbiA9IGN1c3RvbV9kaWN0X2pveSkKc2ltcGxlX3Bsb3QodGV4dF90b19hbmFseXNlX3ZlY3Rvcl9qb3ksIHRpdGxlID0gcGFzdGUodGV4dF90aXRsZSwgIkpveSIsIHNlcD0iIC0gIikpCiNTYXZpbmcgdGhlIGdyYXBoCnBuZyhwYXN0ZShmaWxlX25hbWVfbm9FeHQsIl9KT1kucG5nIiksIGhlaWdodCA9IDkwMCwgd2lkdGggPSAxNjAwLCByZXMgPSAxMDApCnNpbXBsZV9wbG90KHRleHRfdG9fYW5hbHlzZV92ZWN0b3Jfam95LCB0aXRsZSA9IHBhc3RlKHRleHRfdGl0bGUsICJKb3kiLCBzZXA9IiAtICIpKQpkZXYub2ZmKCkKYGBgCgojIyMgVW4gcGV1IGQnaW5nw6luaWVyaWUKClF1J2VzdC1jZSBxdSdpbCB5IGEgc291cyBsZSBjYXBvdAoKIVsxMDAlIGNlbnRlcl0oaW1hZ2VzL2dhcmFnZS5qcGcpCgpub3VzIGFsbG9ucyByZWdhcmRlciBsZXMgbW90cyBxdWkgb250IMOpdMOpIHV0aWxpc8OpcyAgcGVuZGFudCBsJ2FuYWx5c2UsIGNsYXNzw6lzIHBhciBmcsOpcXVlbmNlCgpgYGB7cn0KdGV4dF90b19hbmFseXNlX3Rva2VuaXNlZCA8LSB0YWJsZSh1bmxpc3Qoc3Ryc3BsaXQodGV4dF90b19hbmFseXNlLCAiXFxXIikpKQpoZWFkKHNvcnQodGV4dF90b19hbmFseXNlX3Rva2VuaXNlZCwgZGVjcmVhc2luZyA9IFQpLCBuPTUwKQpgYGAKCkplIHBldXggZXh0cmFpcmUgdG91cyBsZXMgbW90cyB1dGlsZXMgcG91ciBsJ2FuYWx5ZQoKYGBge3J9CnBvc2l0aXZlX3dvcmRzPWNoYXJhY3RlcigpCnBvc2l0aXZlX3dvcmRzX2N1c3RvbT1jaGFyYWN0ZXIoKQpmb3IoaSBpbiAxOmxlbmd0aCh0ZXh0X3RvX2FuYWx5c2VfdG9rZW5pc2VkKSl7CiAgIyBpZGVudGlmeSBwb3NpdGl2ZSB3b3JkcwogIGlmKGdldF9zZW50aW1lbnQobmFtZXModGV4dF90b19hbmFseXNlX3Rva2VuaXNlZFtpXSksIG1ldGhvZCA9ICJjdXN0b20iLCBsZXhpY29uID0gY3VzdG9tX2RpY3Rfam95KSA+IDApewogICAgcG9zaXRpdmVfd29yZHMgPSBjKHBvc2l0aXZlX3dvcmRzLCBuYW1lcyh0ZXh0X3RvX2FuYWx5c2VfdG9rZW5pc2VkW2ldKSkKICAgIHBvc2l0aXZlX3dvcmRzX2N1c3RvbSA9IGMocG9zaXRpdmVfd29yZHNfY3VzdG9tLCAodGV4dF90b19hbmFseXNlX3Rva2VuaXNlZFtpXSpnZXRfc2VudGltZW50KG5hbWVzKHRleHRfdG9fYW5hbHlzZV90b2tlbmlzZWRbaV0pLCBtZXRob2QgPSAiY3VzdG9tIiwgbGV4aWNvbiA9IGN1c3RvbV9kaWN0X2pveSkpKQogIH0KfQpoZWFkKHNvcnQocG9zaXRpdmVfd29yZHNfY3VzdG9tLCBkZWNyZWFzaW5nID0gVCksIG49NTApCmBgYAoKIyMgQXR0ZW50aW9uISEKClJlZ2FyZG9ucyBsZXMgbW90cyBxdWkgc29udCBjYXTDqWdvcmlzw6lzIGNvbW1lIGRlIGxhIGpvaWUgZCdhcHJwcyBGRUVMCgpgYGB7cn0KaGVhZChGRUVMJHdvcmRbd2hpY2goRkVFTCRqb3k9PTEpXSwgbj01MCkKYGBgCgpMZSBsZXhpcXVlIGVzdCAodnJhaW1lbnQpIGxvaW4gZCfDqnRyIHBhcmZhaXQ6IGRlcyBtb3RzIGNvbW1lIF9jb250ZW50XyBvdSBfZmllcl8gbmUgc29udCBwYXMgY29uc2lkw6lyw6kgY29tbWUgcmVsZXZhbnQgZGUgbGEgam9pZSEKCmBgYHtyfQpGRUVMJGpveVt3aGljaChGRUVMJHdvcmQ9PSJqb2llIildCkZFRUwkam95W3doaWNoKEZFRUwkd29yZD09ImhldXJldXgiKV0KRkVFTCRqb3lbd2hpY2goRkVFTCR3b3JkPT0iY29udGVudCIpXQpGRUVMJGpveVt3aGljaChGRUVMJHdvcmQ9PSJmaWVyIildCmBgYAoKRXQgZG9uYyBsYSBwaHJhc2UgImplIHN1aXMgaGV1cmV1eCwgY29udGVudCBldCBmaWVyIiAob3UgcGx1dMO0dCBgamUgw6p0cmUgaGV1cmV1eCwgY29udGVudCBldCBmaWVyYCkgYSB1biBzY29yZSBkZSAxLgoKYGBge3J9CiNqZSByZW50cmUgbW9uIHRleHRlCnRleHRfdG9fYW5hbHlzZV90ZXN0IDwtImplIHN1aXMgaGV1cmV1eCwgY29udGVudCBldCBmaWVyIgojSmUgdG9rZW5pc2UKdGV4dF90b19hbmFseXNlX3Rva2VuaXNlZF90ZXN0IDwtIHRhYmxlKHVubGlzdChzdHJzcGxpdCh0ZXh0X3RvX2FuYWx5c2VfdGVzdCwgIlxcVyIpKSkKI0plIHJlbWV0cyBtZXMgdmFyaWFibGVzIMOgIHplcm8KcG9zaXRpdmVfd29yZHM9Y2hhcmFjdGVyKCkKcG9zaXRpdmVfd29yZHNfY3VzdG9tPWNoYXJhY3RlcigpCiNKZSBjaGVyY2hlIGRhbnMgbGUgbGV4aXF1ZQojUG91ciBjaGFxdWUgbW90IGRlIG1hIHBocmFzZQpmb3IoaSBpbiAxOmxlbmd0aCh0ZXh0X3RvX2FuYWx5c2VfdG9rZW5pc2VkX3Rlc3QpKXsKICAjIHNpIHVuIG1vdCBhIHVuZSB2YWxldXIgc3Vww6lyaWV1cmUgw6AgesOpcm8gZGFucyBtb24gbGV4aXF1ZQogIGlmKGdldF9zZW50aW1lbnQobmFtZXModGV4dF90b19hbmFseXNlX3Rva2VuaXNlZF90ZXN0W2ldKSwgbWV0aG9kID0gImN1c3RvbSIsIGxleGljb24gPSBjdXN0b21fZGljdF9qb3kpID4gMCl7CiAgICAjSmUgbCdham91dGUgYXV4IG1vdHMgcG9zaXRpZnMKICAgIHBvc2l0aXZlX3dvcmRzID0gYyhwb3NpdGl2ZV93b3JkcywgbmFtZXModGV4dF90b19hbmFseXNlX3Rva2VuaXNlZF90ZXN0W2ldKSkKICAgIHBvc2l0aXZlX3dvcmRzX2N1c3RvbSA9IGMocG9zaXRpdmVfd29yZHNfY3VzdG9tLCAodGV4dF90b19hbmFseXNlX3Rva2VuaXNlZF90ZXN0W2ldKmdldF9zZW50aW1lbnQobmFtZXModGV4dF90b19hbmFseXNlX3Rva2VuaXNlZF90ZXN0W2ldKSwgbWV0aG9kID0gImN1c3RvbSIsIGxleGljb24gPSBjdXN0b21fZGljdF9qb3kpKSkKICB9Cn0KcG9zaXRpdmVfd29yZHNfY3VzdG9tCmBgYAoKIyMgUGV1cgoKSmUgcGV1eCByZWNvbW1lbmNlciBsYSBtw6ptZSDDqXR1ZGUsIG1haXMgY2V0dGUgZm9pcyBhdmVjIGxlIGxleGlxdWUgZGUgbGEgIHBldXIKCmBgYHtyfQpjdXN0b21fZGljdF9mZWFyIDwtIGRhdGEuZnJhbWUod29yZD1GRUVMJHdvcmQsIHZhbHVlPUZFRUwkZmVhcikKYGBgCgpFdCBkb25jIHJlcHJvZHVyaWUgdW5lIG3Dqm1lIHZpc3VhbGlzYXRpb24gw6AgcGFydGlyIGRlIGNlIG5vdXZlYXUgc291cy1sZXhpcXVlCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CiN7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9LCBkcGk9MTB9CnRleHRfdG9fYW5hbHlzZV92ZWN0b3JfZmVhciA8LSBnZXRfc2VudGltZW50KHRleHRfdG9fYW5hbHlzZSwgbWV0aG9kID0gImN1c3RvbSIsIGxleGljb24gPSBjdXN0b21fZGljdF9mZWFyKQpzaW1wbGVfcGxvdCh0ZXh0X3RvX2FuYWx5c2VfdmVjdG9yX2ZlYXIsIHRpdGxlID0gcGFzdGUodGV4dF90aXRsZSwgIkZlYXIiLCBzZXA9IiAtICIpKQojU2F2aW5nIHRoZSBncmFwaApwbmcocGFzdGUoZmlsZV9uYW1lX25vRXh0LCJfRkVBUi5wbmciKSwgaGVpZ2h0ID0gOTAwLCB3aWR0aCA9IDE2MDAsIHJlcyA9IDEwMCkKc2ltcGxlX3Bsb3QodGV4dF90b19hbmFseXNlX3ZlY3Rvcl9mZWFyLCB0aXRsZSA9IHBhc3RlKHRleHRfdGl0bGUsICJGZWFyIiwgc2VwPSIgLSAiKSkKZGV2Lm9mZigpCmBgYAoKRXh0cmFpcmUgbGVzIG1vdHMgdXRpbGlzw6lzIHBvdXIgbCdhbmFseXNlCgpgYGB7cn0KcG9zaXRpdmVfd29yZHM9Y2hhcmFjdGVyKCkKcG9zaXRpdmVfd29yZHNfY3VzdG9tPWNoYXJhY3RlcigpCmZvcihpIGluIDE6bGVuZ3RoKHRleHRfdG9fYW5hbHlzZV90b2tlbmlzZWQpKXsKICAjIGlkZW50aWZ5IHBvc2l0aXZlIHdvcmRzCiAgaWYoZ2V0X3NlbnRpbWVudChuYW1lcyh0ZXh0X3RvX2FuYWx5c2VfdG9rZW5pc2VkW2ldKSwgbWV0aG9kID0gImN1c3RvbSIsIGxleGljb24gPSBjdXN0b21fZGljdF9mZWFyKSA+IDApewogICAgcG9zaXRpdmVfd29yZHMgPSBjKHBvc2l0aXZlX3dvcmRzLCBuYW1lcyh0ZXh0X3RvX2FuYWx5c2VfdG9rZW5pc2VkW2ldKSkKICAgIHBvc2l0aXZlX3dvcmRzX2N1c3RvbSA9IGMocG9zaXRpdmVfd29yZHNfY3VzdG9tLCAodGV4dF90b19hbmFseXNlX3Rva2VuaXNlZFtpXSpnZXRfc2VudGltZW50KG5hbWVzKHRleHRfdG9fYW5hbHlzZV90b2tlbmlzZWRbaV0pLCBtZXRob2QgPSAiY3VzdG9tIiwgbGV4aWNvbiA9IGN1c3RvbV9kaWN0X2ZlYXIpKSkKICB9Cn0KaGVhZChzb3J0KHBvc2l0aXZlX3dvcmRzX2N1c3RvbSwgZGVjcmVhc2luZyA9IFQpLCBuPTUwKQoKYGBgCmAKCiMjIyBSZWZlcmVuY2VzClVuIGdyYW5kIG1lcmNpIMOgIFNpbW9uZSBSZWJvcmEgKFVuaXZlcnNpdMOgIGRpIFZlcm9uYS9ESExhYiBCYXNlbCkgcG91ciBzb24gYWlkZS4=